﻿#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

// lock_guard 和 unique_lock
// 什么是lock_guard、unique_lock?
// 在某些特殊的场景下，对于锁的释放不好处理，容易出现死锁问题，所以提出了使用lock_guard解决锁的释放

void Func(int& count, std::mutex& mtx, int num)
{
	mtx.lock();
	throw "Unkown error!";
	for (int i = 0; i < num; i++)
	{
		++count;
	}
	mtx.unlock();
}

void Test1()
{
	// 先实现一个异常环境
	//try
	//{
	//	// 抛异常：除零错误
	//	throw "divide zero!";
	//}
	//catch (const char* errStr)
	//{
	//	std::cout << errStr << std::endl;
	//}
	//catch (...)
	//{
	//	std::cout << "Unkown error!" << std::endl;
	//}


	//int count = 0;
	//// ++次数
	//int num = 0;
	//std::cin >> num;
	//std::thread t1([&count, num]()
	//	{
	//		for (int i = 0; i < num; i++)
	//		{
	//			++count;
	//		}
	//	}
	//);

	//std::thread t2([&count, num]()
	//	{
	//		for (int i = 0; i < num; i++)
	//		{
	//			++count;
	//		}
	//	}
	//);

	//t1.join();
	//t2.join();

	//std::cout << count << std::endl;


	// 在上面的这个多线程中是存在线程安全问题的
	// 所以当我们采用锁
	//int count = 0;
	//std::mutex mtx;
	//// ++次数
	//int num = 0;
	//std::cin >> num;
	//std::thread t1([&count, num, &mtx]()
	//	{
	//		mtx.lock();
	//		for (int i = 0; i < num; i++)
	//		{
	//			++count;
	//		}
	//		mtx.unlock();
	//	}
	//);

	//std::thread t2([&count, num, &mtx]()
	//	{
	//		mtx.lock();
	//		for (int i = 0; i < num; i++)
	//		{
	//			++count;
	//		}
	//		mtx.unlock();
	//	}
	//);

	//t1.join();
	//t2.join();

	//std::cout << count << std::endl;


	// 采用锁之后，线程安全问题确实得到了解决
	// 但是如果加锁和解锁之间抛了一个异常呢？
	int count = 0;
	std::mutex mtx;
	// ++次数
	int num = 20000;
	try
	{
		std::thread t1(Func, std::ref(count), std::ref(mtx), num);
		std::thread t2(Func, std::ref(count), std::ref(mtx), num);

		t1.join();
		t2.join();
	}
	catch (const char* errStr)
	{
		std::cout << errStr << std::endl;
	}
	catch (...)
	{
		std::cout << "???" << std::endl;
	}
	std::cout << count << std::endl;
}


void Func1(std::vector<int>& v, std::mutex& mtx, int base, int num)
{
	// 构造lock_guard对象的时候就会进行加锁(当对象析构的时候，就会自动解锁)
	// std::lock_guard<std::mutex> guard(mtx);
	// unique_lock和lock_guard区别就是：unique_lock比lock_guard多了一些控制接口
	std::unique_lock<std::mutex> guard(mtx);
	// mtx.unlock();
	// mtx.lock();
	// mtx.lock();
	try
	{
		int* ptr = new int;
		for (int i = 0; i < num; i++)
		{
			v.push_back(base + i); // 出现插入失败，抛出一个异常
			if (i == 100)
			{
				throw "bad_alloc!";
			}
			std::cout << std::this_thread::get_id() << "->" << base + i << std::endl;
		}
		// mtx.unlock();
		delete ptr;
	}
	catch (const char* str)
	{
		std::cout << str << std::endl;
		// 死锁问题(lock_guard)
		// mtx.unlock(); 
		// 内存泄漏(使用智能指针解决)
		// delete ptr;
	}
}

void Test2()
{
	// 这里的异常外面捕获的话，主线程会直接终止的（模拟死锁的时候）
	int base1 = 500;
	int base2 = 100000;
	int num = 10000;
	std::mutex mtx;
	std::vector<int> v;
	std::thread t1, t2;
	// 线程中的可执行函数中的参数不能为引用，除非使用ref

	t1 = std::thread(Func1, std::ref(v), std::ref(mtx), base1, num);
	t2 = std::thread(Func1, std::ref(v), std::ref(mtx), base2, num);

	t1.join();
	t2.join();
}

// 条件变量
// condition_varible(条件变量的使用：两个线程交替打印奇数和偶数)
// 这里我们需要先处理线程安全问题，然后再控制奇数和偶数打印
// 注意：条件变量一定要搭配（互斥）锁使用，因为条件变量是线程不安全的（条件变量和互斥锁都不可以拷贝）
void Test3()
{
	int count = 0;
	int num = 100;
	// 互斥锁
	std::mutex mtx;
	std::condition_variable cv;
	bool flag = false;

	// 创建一号线程和二号线程
	// 打印奇数（从0开始）
	std::thread t1([&cv, &count, &flag, &mtx, num]
		{
			while (count < num)
			{
				// 创建unique_lock
				std::unique_lock<std::mutex> lock(mtx);
				// 设置条件变量
				cv.wait(lock, [&flag]()
					{
						return flag;
					}
				);

				std::cout << std::this_thread::get_id() << "->" << count << std::endl;
				++count;

				flag = false;
				cv.notify_one();
			}
		}
	);

	std::thread t2([&cv, &count, &mtx, &flag, num]
		{
			while (count < num)
			{
				std::unique_lock<std::mutex> lock(mtx);
				cv.wait(lock, [&flag]() {return !flag; });

				std::cout << std::this_thread::get_id() << "->" << count << std::endl;
				++count;

				flag = true;
				cv.notify_one();
			}
		}
	);

	t1.join();
	t2.join();
}

// 异常
// 为什么会存在异常？异常和错误码
// 异常是C++从其它语言学习过来的，它有十分巨大的作用
void Test4()
{
	// 异常对象可以是任何类型(包括自定义类型)
	try
	{
		// throw "Division zero!";
		// throw 1.1;
		// throw 1;
		throw 1.1f;
	}
	catch (const char* errStr)
	{
		std::cout << errStr << std::endl;
	}
	catch (float num)
	{
		std::cout << "float num: " << num << std::endl;
	}
	catch (double num) // 默认的小数为double型
	{
		std::cout << "double num: " << num << std::endl;
	}
	catch (int count)
	{
		std::cout << "count: " << count << std::endl;
	}
	catch (...)
	{
		std::cout << "Unknown error!" << std::endl;
	}
	// ...表示任意的异常。注意：只要抛了异常就一定需要捕获，不然就会报错。
	// ...就是用来处理未知异常的，一般都要带上
	// 虽然异常对象可以是任何类型，但是在大型项目中，都会有一套自己的异常继承体系
	// 可以直接使用基类的指针或者引用获取派生类对象
}

// 异常体系

class Exception
{
public:
	Exception(std::string name)
		:_name(name)
	{}

	void What() const
	{
		std::cout << _name << std::endl;
	}
protected:
	std::string _name;
};

class BadAlloc :public Exception
{
public:
	BadAlloc()
		:Exception("bad_alloc")
	{}
};

class DivideExceptio :public Exception
{
public:
	DivideExceptio()
		:Exception("divide_zero")
	{}
};

class OverFlowException :public Exception
{
public:
	OverFlowException()
		:Exception("overflow")
	{}
};

class SqlException :public Exception
{
public:
	SqlException()
		:Exception("sql_error")
	{}
};


void Test5()
{
	try
	{
		// throw OverFlowException();
		// throw BadAlloc();
		// throw DivideExceptio();
		throw SqlException();
	}
	catch (const Exception& pError)
	{
		pError.What();
	}
	catch (...)
	{
		std::cout << "Unknown error!" << std::endl;
	}
}

// 一般情况下异常应该放在外层统一处理，但是特殊情况只能将异常放在内部
// 注意：构造函数和析构函数最好不要抛异常，可能会导致对象初始化不完整或者析构不完全
void Func2() noexcept(false)
{
	for (int i = 0; i < 10; i++)
	{
		bool flag = true;
		std::string str;
		// throw "请求资源不存在";
		try
		{
			throw "请求资源不存在";
		}
		catch (const char* errStr)
		{
			flag = false;
			str = static_cast<std::string>(errStr);
		}
		std::cout << "请不要跳过我！" << std::endl;

		if (flag == false)
		{
			throw str;
		}
	}
}

double Divide(int lhs, int rhs) noexcept(false)
{
	if (rhs == 0)
	{
		throw "Divide zero!";
	}

	// 除数不为零，可以计算返回
	return static_cast<double>(lhs) / static_cast<double>(rhs);
}

void Func3() noexcept(false)
{
	int lhs = 100;
	int rhs = 0;
	int* array = new int[10];

	try
	{
		std::cout << Divide(lhs, rhs) << std::endl;
	}
	catch (const char* errStr)
	{
		// 很挫
		delete[] array;
		throw;
	}
}

void Test6()
{
	Func2();
}

int main()
{
	// Test1();
	// Test2();
	// Test3();
	// Test4();
	// Test5();
	try
	{
		Test6();
	}
	catch (const std::string& str)
	{
		std::cout << str << std::endl;
	}
	catch (const char* str)
	{
		std::cout << str << std::endl;
	}
	catch (...)
	{
		std::cout << "Unknown error!" << std::endl;
	}


	return 0;
}
